import { Service, Container } from 'typedi';
import { CreateDictWithItemsDto, DictPageDto, UpdateDictWithItemsDto } from '@/dtos';
import { DictFindOneResult, DictPageResult, DictWithItems } from '@/interfaces';
import { PrismaClient } from '@/generated/prisma';
import {
  DictNotAllowedToCreateException,
  DictNotAllowedToDeleteException,
  DictNotAllowedToUpdateException,
  DictNotFoundException,
  ServerErrorException,
} from '@/exceptions';
import { dictItemFormatter, dictKeysFormatter } from '@utils/formatter';

@Service()
export class DictService {
  public prisma: PrismaClient = Container.get('PRISMA_CLIENT');

  async createWithItems(dto: CreateDictWithItemsDto): Promise<DictWithItems> {
    const { items, ...dictData } = dto;

    if (await this.prisma.sysDict.findUnique({ where: { code: dictData.code } })) throw new DictNotAllowedToCreateException();

    return this.prisma.$transaction(async tx => {
      const dict = await tx.sysDict.create({ data: dictData });
      if (items.length) {
        await tx.sysDictItem.createMany({
          data: items.map(it => ({
            dictId: dict.id,
            label: it.label,
            value: it.value,
            status: it.status ?? true,
          })),
        });
      }
      return tx.sysDict.findUnique({
        where: { id: dict.id },
        include: { items: true },
      }) as Promise<DictWithItems>;
    });
  }

  async updateWithItems(code: string, dto: UpdateDictWithItemsDto): Promise<DictWithItems> {
    const { items, id, ...dictData } = dto;
    const result = await this.prisma.sysDict.findUnique({ where: { id } });
    if (result.isSystem) throw new DictNotAllowedToUpdateException();

    return this.prisma.$transaction(async tx => {
      // 1. 更新字典主体
      const dict = await tx.sysDict.update({
        where: { code },
        data: dictData,
      });

      // 2. 先清空旧 items
      await tx.sysDictItem.deleteMany({ where: { dictId: dict.id } });

      // 3. 批量写入新 items
      if (items.length) {
        await tx.sysDictItem.createMany({
          data: items.map(it => ({
            dictId: dict.id,
            label: it.label,
            value: it.value,
            status: it.status ?? true,
          })),
        });
      }

      // 4. 返回最新数据
      return tx.sysDict.findUnique({
        where: { id: dict.id },
        include: { items: true },
      }) as Promise<DictWithItems>;
    });
  }

  async getDictKeys() {
    const result = await this.prisma.sysDict.findMany({ where: { status: true } });
    return dictKeysFormatter(result);
  }

  async findOneWithItems(code: string, all = false): Promise<DictFindOneResult> {
    const dict = await this.prisma.sysDict.findUnique({
      where: {
        code,
        ...(all ? {} : { status: true }),
      },
      include: { items: true },
    });
    if (!dict) throw new DictNotFoundException();
    return dictItemFormatter(dict, all);
  }

  async findAllWithItems(pageDto: DictPageDto): Promise<DictPageResult> {
    const where = pageDto.keyword
      ? {
          OR: [{ name: { contains: pageDto.keyword } }, { code: { contains: pageDto.keyword } }],
        }
      : {};

    const [list, total] = await Promise.all([
      this.prisma.sysDict.findMany({
        skip: pageDto.offset,
        take: pageDto.pageSize,
        where: {
          ...where,
          status: pageDto.status,
        },
        orderBy: { id: 'asc' },
      }),
      this.prisma.sysDict.count({ where }),
    ]);
    return { list, total };
  }

  async remove(code: string) {
    const dict = await this.findOne(code);
    if (dict.isSystem) throw new DictNotAllowedToDeleteException();
    try {
      return this.prisma.sysDict.delete({ where: { code } });
    } catch (e: any) {
      // 无记录删除
      if (e.code === 'P2025') return;
      throw new ServerErrorException(e);
    }
  }

  private async findOne(code: string) {
    const dict = await this.prisma.sysDict.findUnique({ where: { code } });
    if (!dict) throw new DictNotFoundException();
    return dict;
  }
}
